home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MACD 5
/
MACD 5.bin
/
workbench
/
tools
/
czesc_1
/
dr
/
source
/
dr.c
next >
Wrap
C/C++ Source or Header
|
1993-11-21
|
59KB
|
2,384 lines
/* TO DO:
option letters available: E G J Q W .... maybe:
q = supercompaQt columns
g = aGe
j = Jump into soft links when -R / ::?
Filter cases where a multi-assign was already covered by recursion?
Hard links in -r listing can end up with linkee's name??
Add \:::f and \:::p? \" for quote if space in pathname?
Someday make -P default to -P~H instead of no mask, if C= ever does likewise.
Add date-string parsing to -A and -B with syntax -A(date time). -Adays.hours?
Make it show both -X info and -R info when both are specified.
Make -P able to have or-ed sets of bits, like -p~as|~ap ?
-G shows file's age in days hh:mm:ss?
Option -Q for super-compact columnation? Always use super-compact?
Avoid repeating "unknown option" for same letter?
Note: when -R goes into a soft link to a file (presently disabled), it shows
the file as if it were inside the soft link, rather than being what it's
linked to.
*/
/* ==========================================================================
The idea here is to make Yet Another Cli Directory Command. What's special
about this one? It's really really fast, it's got more goddamn features than
you can believe, and by default it doesn't show .info files. Instead it just
shows all files that have .info's associated with them by writing the name in
orange instead of white when output is to the screen. And it puts file names
(and directory names also) in as many columns (up to ten) as will fit
comfortably in the window. And everything is alphabetized in columns. It will
also do Amiga patterns. It tries a pattern first as an exact literal before
expanding it, in case you have a drawer named "Doesn't work?" or something. And
it does recursive descents, faster than the Fast File System. It is intended to
replace Dir, List, rls, and du, and to outperform all competitors.
Dr is written for Aztec C 5.2b for Amiga, by Paul Kienitz. Public Domain. See
the files Dr.doc and FastExNext.doc for useful information. Documentation for
smallio.c is in the source, kind of. Look in pureio.c (included with older
versions of Dr) if you really want to know more.
========================================================================== */
/* some #defines which you can make different verisons with:
if C_NOT_ASM it does not use inline assembly language instead of C
for some sorting functions (which won't work with Lattice/SAS).
if DEBUG is defined it may produce extra error messages
if MARKLINKS is defined then a -L style output will mark hard and soft
links with H> and S>.
*/
#define VERSION "2.0"
#define FLACK ':' /* NOT USED */
#define WIDEFAULT 77
#define HYPH 0x96
#define HELP 0x9F
#define CSI "\x9B"
#define MAXCOLS 10
#define PREPENGTH 300
#define LOOPDEPTH 30
#define STACKNEEDED (1500 + 400)
#define PATLIMIT 128
#define ABSIZE 300L
#define SOFTSIZE 288L
#define MULTILIMIT 50
#define TOPSTYLE 3
/* styles: 0 = dented, 1 = blankline, 2 = stupid dashes, 3 = mingled */
/* WIDEFAULT is the assumed output width when we can't measure the window.
FLACK is char used to mark icon'd files in output. Not used these days.
HYPH is used to mark option arguments for mane.
CSI is the one-char string that starts "escape sequences"; same as esc [.
MAXCOLS is the max number of text columns to stack listed names in.
PREPENGTH is the maximum length of pathnames labelling recursive levels.
LOOPDEPTH is how far back we check for hard-link looping.
STACKNEEDED is the amount of stack space needed to scan a directory.
PATLIMIT is the maximum length of pattern strings.
ABSIZE is the maximum length of paths converted by the -T option.
SOFTSIZE is the number of characters allowed for a soft link's path string.
MULTILIMIT is the number of locks that can be handled in a multiple assign.
*/
#include <exec/types.h>
#include <exec/io.h>
#include <exec/memory.h>
#include <dos/dosextens.h>
#include <dos/dostags.h>
#include <dos/datetime.h>
#include <clib/dos_protos.h>
#include <clib/exec_protos.h>
#include <clib/alib_protos.h>
#include <pragmas/dos_lib.h>
#include <pragmas/exec_lib.h>
#include <ctype.h>
#include <string.h>
#include <Paul.h>
#ifdef put
# undef put
#endif
/* Here we have a simplified version of <devices/conunit.h> which does not
pull in stuff like struct Window and struct TextFont: */
struct ConUnit {
short pad[21];
short cu_XMax, cu_YMax;
};
/* pretty simplified, wasn't it */
typedef struct _fly *flip;
typedef struct _fly {
flip next; /* list link */
long length, blox, tection;
struct DateStamp when;
long keee;
str comment;
short ordination;
char name[31];
/* these are the most heavily used flags: */
char /* bool */ jected, infoed, wanted, dirred, softlink;
ushort fflags; /* ... and these are the less used ones */
str slinky;
} fly; /* size exactly 80 bytes always */
#define F_HARDLINK 1
#define F_DESCEND 2
#define F_ROOT 4
struct cuont {
long blok, byt, fil, dir;
};
adr stacklimit;
str argline;
str *argv;
int arglen, argc, hyphc;
short cwid, song, keygits, cols, wid, abort, rdepth, steil = 0;
bool color, curse, cize, complete, cron, cons, cutdirs, ceys, cutfils, colorful;
bool /* cage, */ canydepth, csternal, cortless, consumption, ctifle, cweeek;
bool creverse, colsort, cizesort, cursorhide, convertpath, cancelenvar, cupside;
bool zize, zomplete, /* zons, FLACK */ zurse, zutdirs, zutfils, zortless;
bool zuttop, zutbot;
long protlook, protwant, before, after, tooday;
str oform, xform;
struct ConUnit *cuca;
struct Process *me;
long hair;
ubyte mesh[PATLIMIT * 2 + 3];
char abspath[ABSIZE];
struct AnchorPath *ankh;
bool patty, flipat, didaninny, needsnl, anyfiles, morethan1;
bool notadir, notadirbutokay;
#ifdef MARKLINKS
bool anylinks;
#endif
char prepath[PREPENGTH];
struct Ants {
struct Ants *prav;
long kay;
};
struct DosLibrary *DOSBase;
char versionstring[] = "\0$VER: Dr " VERSION " (" __DATE__ ")\n\r";
char helpslab[] =
" -C sort from oldest to newest -D show only dirs, not files\n"
" -F show only files, not dirs -H sort in rows, not columns\n"
" -I show .info files normally -K show disk addresses\n"
" -L show size protection date etc. -M don't use color to show icons\n"
" -O one complete pathname per line -R show contents of all subdirs\n"
" -S show length of each file -T convert paths to absolute form\n"
" -U just show disk space usage -V sort in reverse order\n"
" -X show date etc, not contents -Y use day names for recent dates\n"
" -Z sort from smallest to largest -? no output; return 5 if no match\n"
" -@ don't check DR-OPTS variable -! turn off cursor for faster output\n"
" -A# (e.g. -A7) show files and dirs changed in the last # days\n"
" -B# (e.g. -B30) show those changed more than # days ago\n"
" -N# (e.g. -N3) select style for arranging output of files and dirs\n"
" -Pb where b is one or more of H S P A R W E D with optional ~ in front\n"
" of letters (e.g. -PS~A~D): don't show non-matching protection bits\n"
" -[...string...] describes format of output -- see docs for description\n"
" of special codes preceded with \"\\\".\n"
" -{...string...} is like -[...] but result is executed as a command\n";
#define SIGMASK (long) (SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D)
ubyte sorder[256] = "\0\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17"
"\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37"
" !\"#$%&'()*+,-./0123456789:;<=>?"
"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
"`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~\177"
"\200\201\202\203\204\205\206\207"
"\210\211\212\213\214\215\216\217"
"\220\221\222\223\224\225\226\227"
"\230\231\232\233\234\235\236\237"
" ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿"
"AAAAAAACEEEEIIIIDNOOOOO×OUUUUYÞS"
"AAAAAAACEEEEIIIIDNOOOOO÷OUUUUYÞY";
/* for correct sorting: "Mädchen" between "madam" and "madman". */
struct pack {
struct FileInfoBlock fibuf;
struct StandardPacket sp;
struct MsgPort repp;
bool isout;
BPTR lock;
};
/* ================== functions: ================== */
#pragma amicall(DOSBase, 0xf0, DoPkt3(d1,d2,d3,d4,d5))
#ifdef LEAKAGE
import adr AllocYell(long a, long b, str c, long d);
import void FreeYell(adr a, long b, str c, long d);
#define AllocMem(a, b) AllocYell((long) a, (long) b, __FUNC__, (long) __LINE__)
#define FreeMem(a, b) FreeYell(a, (long) b, __FUNC__, (long) __LINE__)
#define _AllocMem(a, b) AllocYell((long) a, (long) b, __FUNC__, (long) __LINE__)
#define _FreeMem(a, b) FreeYell(a, (long) b, __FUNC__, (long) __LINE__)
#endif
import bool OpenSmallIO(void (*ff)());
import void CloseSmallIO(void), puch(ushort c), put(str s),
putfmt(str format, ...), pflush(void), StopAllOutput(void);
#define lower(C) ((C) | 0x20)
/* for forward references: */
void DoInner(str n, BPTR l, bool hasparent,
struct Ants *ants, struct cuont *great);
void Cough1(register flip y, bool dirs, short *col);
void FloorMat(register flip y, bool xeq);
private void btoc(str c, ubyte *b, short lim) /* b == c is okay */
{
register ushort l = *b;
if (l > lim) l = lim;
strncpy(c, (str) b + 1, (size_t) l);
c[l] = '\0';
}
private void SendExNext(register struct pack *p)
{
register struct DosPacket *d = &p->sp.sp_Pkt;
register short l = strlen(p->fibuf.fib_FileName);
register str s = &p->fibuf.fib_FileName[l];
while (s > &p->fibuf.fib_FileName[0]) /* un-btoc() */
*s = s[-1], s--;
*(ubyte *) s = l;
/* the filesystem ralphs if you send a null lock to pr_FileSystemTask? */
p->sp.sp_Msg.mn_Node.ln_Name = (adr) d;
d->dp_Link = &p->sp.sp_Msg;
d->dp_Port = p->sp.sp_Msg.mn_ReplyPort = &p->repp;
d->dp_Type = ACTION_EXAMINE_NEXT;
d->dp_Arg1 = p->lock;
d->dp_Arg2 = ((long) &p->fibuf) >> 2;
p->isout = true;
PutMsg(bip(struct FileLock, p->lock)->fl_Task, d->dp_Link);
}
private void WaitExNext(struct pack *p)
{
register struct Message *m;
while (p->isout) {
WaitPort(&p->repp);
while (m = GetMsg(&p->repp))
if (m == &p->sp.sp_Msg) {
p->isout = false;
break;
}
}
btoc(p->fibuf.fib_FileName, (ubyte *) p->fibuf.fib_FileName, 30);
btoc(p->fibuf.fib_Comment, (ubyte *) p->fibuf.fib_Comment, 79);
}
void AsExCleanup(struct pack **pk)
{
struct pack *p = *pk;
if (p && ~(long) p) {
WaitExNext(p); /* you can't AbortIO a packet */
FREE(p);
*pk = (adr) ~0;
}
}
long AsExamine(BPTR lok, struct FileInfoBlock *fibb, struct pack **pk)
{
long r;
*pk = null;
r = Examine(lok, fibb);
if (!lok || !r || fibb->fib_EntryType < 0)
return r;
if (NEWPZ(*pk)) {
register struct pack *p = *pk;
p->repp.mp_Node.ln_Type = NT_MSGPORT;
p->repp.mp_Flags = PA_SIGNAL;
p->repp.mp_SigBit = SIGB_DOS;
p->repp.mp_SigTask = me;
NewList(&p->repp.mp_MsgList);
p->repp.mp_MsgList.lh_Type = NT_MSGPORT; /* what the heck */
p->fibuf = *fibb; /* copy of struct, not pointer */
p->isout = false;
p->lock = lok;
SendExNext(p);
}
return r;
}
long AsExNext(BPTR lok, struct FileInfoBlock *fibb, struct pack **pk)
{
long ret;
struct pack *p = *pk;
if (!~(long) p) {
me->pr_Result2 = ERROR_NO_MORE_ENTRIES;
return 0;
}
if (!p)
return ExNext(lok, fibb);
WaitExNext(p);
*fibb = p->fibuf;
ret = p->sp.sp_Pkt.dp_Res1;
me->pr_Result2 = p->sp.sp_Pkt.dp_Res2;
if (!ret && me->pr_Result2 == ERROR_NO_MORE_ENTRIES)
AsExCleanup(pk);
else
SendExNext(p); /* continue in spite of error */
return ret;
}
void putn(str s)
{
put(s);
puch('\n');
}
long StackLeft(void)
{
short i;
return (long) &i + 14 - (long) stacklimit;
}
short digits(register ulong l)
{
register short d = 0;
while (l) {
d++;
l /= 10;
}
return (d ? d : 1);
}
void pad(short w)
{
while (--w >= 0)
puch(' ');
}
void padong(ulong n, short w)
{
pad(w - digits(n)); /* RawDoFmt doesn't have %*ld */
putfmt("%ld", n);
}
/* RawDoFmt doesn't have %lu either, so let's just hope n is always positive */
void fortection(str s, register ulong bits)
{
register short b;
bits ^= 15;
strcpy(s, "hsparwed");
for (b = 0; b <= 7; b++)
if (!(bits & bit(b)))
s[7 - b] = '-';
s[8] = 0;
}
#asm
public _mob
_mob: move.b d0,(a3)+
rts
#endasm
void formdate(str s, struct DateStamp *when, short space)
{
char temp[20];
struct DateTime dt;
dt.dat_Stamp = *when;
dt.dat_Format = FORMAT_DOS; /* FORMAT_DEF ? */
dt.dat_Flags = cweeek ? DTF_SUBST : 0;
dt.dat_StrDay = null;
dt.dat_StrDate = temp;
dt.dat_StrTime = temp + 10; /* adjust this if FORMAT_other */
DateToStr(&dt);
temp[9] = ' ';
temp[18] = 0;
while (space > 18)
*s++ = ' ', space--;
strncpy(s, temp, space);
s[space] = 0;
}
/* Another personal ad found in EXPRESS "The East Bay's Free Weekly":
HI.
Yup, that was the whole ad. Right after it:
RALPH, a 1967 Cadillac, now accepting devotees.
*/
void Lose(register flip f)
{
if (f) {
if (f->comment)
FreeMem(f->comment, 80L);
if (f->slinky && f->softlink > 0)
FreeMem(f->slinky, SOFTSIZE);
FREE(f);
}
}
void CCch(void)
{
if (abort < 5 && (SetSignal(0L, SIGMASK) & SIGMASK)) {
abort = 5;
StopAllOutput();
Write(Output(), "\n *** BREAK\n", 12L);
me->pr_Result2 = 0;
}
}
void PFault(str head)
{
pflush();
if (hair == ERROR_TOO_MANY_LEVELS)
putfmt("%s: not enough stack space.\n", head);
else
PrintFault(hair, head);
}
flip /* flop & */ Fly(register struct FileInfoBlock *b)
{
register flip z;
register short tt = b->fib_DirEntryType;
if (!NEW(z))
return null;
z->comment = null; /* if alloc fails, no big deal */
if (*b->fib_Comment && (z->comment = Alloc(80)))
strcpy(z->comment, b->fib_Comment);
z->next = null;
z->length = b->fib_Size;
z->blox = b->fib_NumBlocks;
z->tection = b->fib_Protection;
z->when = b->fib_Date;
z->keee = b->fib_DiskKey;
strcpy(z->name, b->fib_FileName);
z->fflags = (tt == 4 || tt == -4 ? F_HARDLINK : 0);
z->softlink = tt == 3;
z->dirred = tt > 0;
z->wanted = z->jected = z->infoed = false;
z->slinky = null;
return z;
}
void DoImmediate(flip m, bool immediacy)
{
if (!m->jected) {
if (xform)
FloorMat(m, true);
if (immediacy)
if (oform && !ctifle)
FloorMat(m, false);
else {
short fakecol = 0;
Cough1(m, m->dirred, &fakecol);
}
}
}
flip Scan1(short *ficou, struct FileInfoBlock *deef, BPTR deer, bool noparent)
{
flip result = null;
BPTR dp, ocd, fo;
char fone[31];
if (!(result = Fly(deef)))
return null;
*ficou = 1; /* we're seeing if it has a .info file */
dp = ParentDir(deer);
if ((!cons || !colorful) && strlen(deef->fib_FileName) < 26 && dp) {
ocd = CurrentDir(dp);
strcpy(fone, result->name);
strcat(fone, ".info");
CCch();
if (!abort && (fo = RLock(fone))) {
result->infoed = true;
UnLock(fo);
}
CurrentDir(ocd);
}
/* WE SEEM to be running into an undocumented feature here... the
/* ding bling ParentDir function sometimes sets IoErr = 212 after a
/* perfectly normal and SUCCESSFUL call. When it does this, the
/* following RLock also does so. So we just band-aid it: */
if (me->pr_Result2 == ERROR_OBJECT_NOT_FOUND
|| me->pr_Result2 == ERROR_OBJECT_WRONG_TYPE)
me->pr_Result2 = 0;
if (dp)
UnLock(dp);
else {
result->fflags |= F_ROOT;
result->length = result->tection = 0; /* invalid bits */
}
if (noparent) {
zurse = zutfils = zutdirs = false;
if (!oform && !zortless)
zomplete = true;
notadirbutokay = true;
if (patty)
result->jected = notadir = true;
}
DoImmediate(result, zortless);
return result;
}
bool OutOfDate(flip f)
{
long a;
struct DateStamp d;
if (!(before | after))
return false;
if (!tooday) {
DateStamp((adr) &d);
tooday = d.ds_Days;
}
a = 1 + tooday - f->when.ds_Days;
if (a <= 0) a = 1;
if (before > after)
return a > after && a <= before;
else
return a > after || a <= before;
}
void Descendify(flip f, BPTR deer, struct Ants *pan, struct cuont *great)
{
BPTR ocd = CurrentDir(deer), innerdeer;
if (innerdeer = RLock(f->name)) {
register bool p = patty;
if (!canydepth)
patty = false;
rdepth++;
DoInner(f->name, innerdeer, true, pan, great);
rdepth--;
patty = p;
UnLock(innerdeer);
} else {
hair = me->pr_Result2;
putfmt(" *** Can't lock inner directory \"%s\"!\n", f->name);
}
CurrentDir(ocd);
}
flip ScanInside(short *ficou, struct FileInfoBlock *deef, BPTR deer,
struct Ants *pan, struct cuont *great, adr *potholderptr)
{
flip result = null, more;
long air = 0;
bool prejected, plork;
/* This is for avoiding explosions with ReadLink: */
struct DeviceList *vol = gbip(bip(struct FileLock, deer)->fl_Volume);
bool bogondisk = (vol->dl_DiskType & ~2L) == 0x444F5300;
/* vvvv prevent multiple "please replace" in -R */
while (hair != ERROR_DEVICE_NOT_MOUNTED
&& AsExNext(deer, deef, potholderptr)) {
CCch();
if (abort || !(more = Fly(deef))) break;
prejected = (more->dirred ? zutdirs : zutfils)
|| (more->tection & protlook) != protwant
|| OutOfDate(more);
plork = (!prejected || (curse && more->dirred && !canydepth))
&& (!patty || MatchPatternNoCase(mesh, more->name));
more->jected = prejected || !plork;
if ((plork || canydepth) && zurse && more->dirred && !more->softlink) {
more->fflags |= F_DESCEND;
if (!cupside)
Descendify(more, deer, pan, great);
}
if (more->softlink && zomplete)
if (!bogondisk && (more->slinky = Alloc(SOFTSIZE))) {
bool r = ReadLink(bip(struct FileLock, deer)->fl_Task,
deer, more->name, more->slinky, SOFTSIZE);
if (!r) {
FreeMem(more->slinky, SOFTSIZE);
more->slinky = (str) - me->pr_Result2;
more->softlink = -1;
}
}
DoImmediate(more, zortless);
more->next = result;
result = more;
(*ficou)++;
}
if (air)
me->pr_Result2 = air;
if (me->pr_Result2 == ERROR_NO_MORE_ENTRIES)
me->pr_Result2 = 0;
return result;
}
/* scans a Dos directory and returns the findings in a linked list of fly's.
*ficou tells the number of elements in the list. */
flip ScanDeer(BPTR deer, short *ficou, bool hasparent,
struct Ants *pan, struct cuont *great)
{
flip result = null, more;
register struct FileInfoBlock *deef;
adr potholder;
*ficou = 0;
if (!NEWP(deef)) {
me->pr_Result2 = ERROR_NO_FREE_STORE;
return null;
}
if (AsExamine(deer, deef, &potholder)) {
CCch();
if (!abort)
if (deef->fib_DirEntryType < 0 || (csternal
&& !patty && !zurse && !consumption))
result = Scan1(ficou, deef, deer, !hasparent);
else
result = ScanInside(ficou, deef, deer, pan, great, &potholder);
}
#ifdef DEBUG
if (me->pr_Result2 == -1)
me->pr_Result2 = 0; /* work around SDB shortcoming */
#endif
if (me->pr_Result2 && !abort) {
hair = me->pr_Result2;
if (!ctifle)
PFault("DR COULDN'T FINISH SCAN");
CCch();
}
AsExCleanup(&potholder);
FREE(deef);
if (abort)
while (result) {
more = result;
result = result->next;
Lose(more);
}
return result;
}
void inatl_strupr(register ustr pp)
{
register ubyte c;
for ( ; c = *pp; pp++)
if ((c >= 'a' && c <= 'z') || (c >= 0xE0 && c != 0xF7 && c != 0xFF))
*pp = c - 32;
}
#ifdef C_NOT_ASM /* redo these in assembly for speed */
short alpha(ubyte *a, ubyte *b)
{
register ubyte ac, bc;
short res, lean = 0;
do {
if (!lean)
lean = (short) *a - (short) *b;
ac = sorder[*(a++)];
bc = sorder[*(b++)];
} while (ac && ac == bc);
res = (short) ac - (short) bc;
return res ? res : lean;
}
short alpo(flip a, flip b)
{
return alpha(a->name, b->name);
}
short olda(flip a, flip b)
{
register long t;
if (t = a->when.ds_Days - b->when.ds_Days)
return t;
if (t = a->when.ds_Minute - b->when.ds_Minute)
return t;
return a->when.ds_Tick - b->when.ds_Tick;
}
short siez(flip a, flip b)
{
if (a->length < b->length)
return -1;
else
return a->length > b->length;
}
short infoo(ubyte *a, ubyte *b)
/* returns 0 if filename b is the .info of filename a, positive if b is
alphabetically after a's .info name, negative if before. */
{
ubyte acat[36];
register ubyte ac, bc;
register short f = (short) sorder[b] - (short) sorder[a];
if (f)
return f;
strcpy(acat, a);
strcat(acat, ".info");
a = acat;
do {
ac = sorder[*(a++)];
bc = sorder[*(b++)];
} while (ac && ac == bc);
return (short) bc - (short) ac;
}
#else
#pragma regcall(alpo(a0, a1))
#pragma regcall(olda(a0, a1))
#pragma regcall(siez(a0, a1))
#pragma regcall(alpha(a0, a1))
#pragma regcall(infoo(a1, a0))
/* ^^ ^^ backwards on purpose */
/* these optimized versions make a noticeable difference when sorting a
directory with 75 files or more */
#asm
public _alpha
public _alpo
public _olda
public _siez
public _infoo
_alpo: lea 38(a0),a0 ; first->name
lea 38(a1),a1 ; second->name
; fall thru:
_alpha: movem.l a2/d2,-(sp)
lea _sorder,a2
moveq #0,d1
moveq #0,d2
nxt: moveq #0,d0
move.b (a0),d0
swap d0
move.b (a0)+,d0 ; get two copies
move.b (a1)+,d1
beq out ; end of string
sub.w d1,d0
beq nxt ; exactly equal chars
tst.w d2
bne leanin ; diff of unsordered bytes
move.w d0,d2 ; if no such already found
leanin: swap d0 ; restore original byte
move.b (a2,d0.w),d0 ; convert by sorder
move.b (a2,d1.w),d1
cmp.b d0,d1
beq nxt ; equal after sorder conversion
out: sub.w d1,d0 ; compare as unsigned bytes
bne unlean
move.w d2,d0 ; use lean if otherwise equal
unlean: movem.l (sp)+,a2/d2
rts
_olda: move.l 16(a0),d0 ; a->when.ds_Days
sub.l 16(a1),d0 ; - b->when.ds_Days
bne zulte
move.l 20(a0),d0 ; a->when.ds_Minute
sub.l 20(a1),d0 ; - b->when.ds_Minute
bne zulte
move.l 24(a0),d0 ; a->when.ds_Tick
sub.l 24(a1),d0 ; - b->when.ds_Tick
zulte: rts
_siez: move.l 4(a0),d0 ; a->length
sub.l 4(a1),d0 ; - b->length
bge nnegrt
moveq #-1,d0
rts
nnegrt: bne posrt
moveq #0,d0
rts
posrt: moveq #1,d0
rts
_infoo: move.l a2,-(sp)
moveq #0,d0
moveq #0,d1
move.b (a0),d0 ; "b"
move.b (a1),d1 ; "a"
lea _sorder,a2
move.b 0(a2,d0.w),d0
move.b 0(a2,d1.w),d1
sub.b d1,d0 ; quick pre-test
beq check
ext.w d0 ; return test diff if nonzero
move.l (sp)+,a2
rts
check: link a5,#-40 ; the real test
move.l sp,a2 ; temporary copy area
cpy: move.b (a1)+,(a2)+
bne cpy
move.b #'.',-1(a2) ; overwrite final nul
move.b #'i',(a2)+
move.b #'n',(a2)+
move.b #'f',(a2)+
move.b #'o',(a2)+
clr.b (a2)
move.l sp,a1
lea _sorder,a2
loeup: move.b (a0)+,d0
move.b (a1)+,d1
beq pueol
move.b (a2,d0.w),d0
move.b (a2,d1.w),d1
cmp.b d1,d0
beq loeup
pueol: sub.w d1,d0
unlk a5
move.l (sp)+,a2
rts
#endasm
import short alpo(flip a, flip b), olda(flip a, flip b), siez(flip a, flip b);
import short alpha(ubyte *a, ubyte *b);
import short infoo(ubyte *a, ubyte *b);
#endif
#ifdef C_NOT_ASM
flip MergeSort(flip flist, short ficou, bool reverse,
short (*sortie)(flip a, flip b), short (*second)(flip a, flip b))
{
register flip l, l1, l2;
flip *p;
register short i, oint, s;
if (ficou <= 2) { /* handle degenerate cases */
if (ficou < 2) return flist;
l = flist->next;
if (!(s = sortie(flist, l)))
s = second(flist, l);
if ((s > 0) ^ reverse) {
flist->next = null;
l->next = flist;
flist = l;
}
return flist;
}
oint = ficou >> 1; /* split list in two */
l1 = l = flist;
for (i = 1; i < oint; i++)
l = l->next;
l2 = l->next;
l->next = null;
l1 = MergeSort(l1, oint, reverse, sortie, second); /* sort each half */
l2 = MergeSort(l2, ficou - oint, reverse, sortie, second);
p = &flist; /* merge sublists */
while (l1 && l2) {
if (!(s = sortie(l1, l2)))
s = second(l1, l2);
if ((s < 0) ^ reverse)
l = l1, l1 = l1->next;
else
l = l2, l2 = l2->next;
*p = l;
p = &l->next;
}
if (l1)
*p = l1;
else
*p = l2;
return flist;
}
#else
#asm
public _MergeSort
regzz reg d2-d7/a2-a6
_MergeSort: movem.l regzz,-(sp)
move.l a0,a5 ; flist
move.l a1,a6 ; sortie
move.l a2,d5 ; second
cmp.w #2,d2 ; ficou
bgt split
bne retflist ; list length 1, return it
move.l (a5),a2 ; length 2, check order
move.l a2,a1 ; ^^ l = flist->next
move.l a5,a0
jsr (a6) ; s = sortie(flist, l)
tst.w d0
bne nosecond ; no difference?
move.l a2,a1
move.l a5,a0
exg d5,a6
jsr (a6) ; s = second(flist, l)
exg a6,d5
nosecond: tst.w d6 ; if (reverse) s = -s
beq noneg1
neg.w d0
noneg1: tst.w d0
ble retflist ; is in correct order
clr.l (a5) ; flist->next = null
move.l a5,(a2) ; l->next = flist
move.l a2,a5 ; flist = l
bra retflist
split: move.w d2,d4 ; save for later
move.w d2,d3
asr.w #1,d3 ; oint = ficou / 2
move.l a5,a2 ; l = flist
move.l a5,a3 ; l1 = flist
move.w d3,d0
subq #1,d0
zoop: dbra d0,zinn ; do oint-1 times
bra zun ; yes we can go through 0 times
zinn: move.l (a2),a2 ; l = l->next
bra zoop
zun: move.l (a2),d7 ; l2 = l->next
clr.l (a2) ; l->next = null
move.l a2,-(sp) ; save
move.l d5,a2 ; second
move.l a6,a1 ; sortie
move.w d3,d2 ; oint
move.l a3,a0 ; l1
bsr _MergeSort ; l1 = MergeSort(l1, oint,
move.l d0,a3 ; reverse, sortie, second)
move.l a6,a1 ; sortie again
move.w d4,d2
sub.w d3,d2 ; ficou - oint
move.l d7,a0 ; l2
bsr _MergeSort ; l2 = MergeSort(l2, ficou - oint,
move.l d0,d7 ; reverse, sortie, second)
move.l (sp)+,a2 ; restore
move.l a5,-(sp) ; put flist in memory
move.l sp,a5 ; p = &flist
elihu: move.l a3,d0 ; pseudo tst.l
beq finnish
move.l d7,d0
beq finnish ; neither list empty?
move.l d7,a1
move.l a3,a0
jsr (a6) ; s = sortie(l1, l2)
tst.w d0
bne nosecond2
move.l d7,a1
move.l a3,a0
exg d5,a6
jsr (a6) ; s = second(l1, l2)
exg a6,d5
nosecond2: tst.w d6
beq noneg2
neg.w d0
noneg2: tst.w d0
bge twooo
move.l a3,a2 ; l = l1
move.l (a3),a3 ; l1 = l1->next
bra onnne
twooo: move.l d7,a2 ; l = l2
exg a0,d7
move.l (a0),a0 ; l2 = l2->next
exg a0,d7
onnne: move.l a2,(a5) ; *p = l
move.l a2,a5 ; p = &l->next
bra elihu
finnish: move.l a3,d0 ; is l1 empty?
beq tuuue
move.l a3,(a5) ; *p = l1
bra retstarp
tuuue: move.l d7,(a5) ; *p = l2
retstarp: move.l (sp)+,a5
retflist: move.l a5,d0
movem.l (sp)+,regzz
rts
#endasm
import flip MergeSort(flip flist, short ficou, bool reverse,
short (*sortie)(flip a, flip b), short (*second)(flip a, flip b));
#pragma regcall(MergeSort(a0, d2, d6, a1, a2))
#endif
flip Soart(flip flist, short ficou)
{
register flip t, tt;
register short k;
if (!flist)
return null;
if (!cons) {
/* alpha sorting greatly speeds the following .info rejector, enough to
justify doing an alpha sort and then doing a different sort afterwards: */
flist = MergeSort(flist, ficou, false, alpo, siez);
for (t = flist; t; t = t->next)
for (tt = t->next; tt; tt = tt->next)
if (!(t->jected | tt->dirred))
if (!(k = infoo((ubyte *) t->name, (ubyte *) tt->name))) {
t->infoed = tt->jected = true;
break;
} else if (k > 0)
break;
}
if (cizesort)
flist = MergeSort(flist, ficou, creverse, siez, (cron ? olda : alpo));
else if (cron)
flist = MergeSort(flist, ficou, creverse, olda, alpo);
else if (!cons || creverse)
flist = MergeSort(flist, ficou, creverse, alpo, siez);
return flist;
}
short CheckWindowWidth(void)
{
struct InfoData *ind;
adr cont = bip(struct FileHandle, me->pr_COS)->fh_Type;
if (!cuca)
if (color && cont && NEWP(ind)) {
if (DoPkt1(cont, (long) ACTION_DISK_INFO, (ulong) ind >> 2))
cuca = (adr) ((struct IOStdReq *) ind->id_InUse)->io_Unit;
else color = false;
FREE(ind);
} else color = false;
return color ? cuca->cu_XMax + 2 : WIDEFAULT + 1;
}
/* I know, there's an escape sequence. But it don't work without you does
set_raw, which does dos_packet, and it comes out smaller this way. I could
probably save 100 bytes or so under 1.3 by doing my own packet handling ... */
/* we actually return the number of characters that will fit plus one. This is
because most calculations relating to window fit have to subtract one to
compensate for the lack of a one space pad after the last column. So it saves
some arithmetic to add the one first. */
void Columnate(flip flist, short ficou)
{
short n, c = 0;
bool filongest = false;
register flip f;
/* bool oneinfo = false; FLACK */
song = keygits = 1;
for (f = flist; f; f = f->next)
if (!f->jected) {
c++;
if ((n = digits(f->length)) > song)
song = n;
if (ceys && (n = digits(f->keee)) > keygits)
keygits = n;
#ifdef MARKLINKS
if ((f->fflags & F_HARDLINK) || f->softlink)
anylinks = true;
#endif
if (!f->dirred)
anyfiles = true;
}
if (consumption || oform || zortless)
return;
cwid = 1;
if (!wid)
wid = CheckWindowWidth();
for (f = flist; f; f = f->next)
if (!f->jected) {
n = strlen(f->name) + 1 /* 2 - cons */ ; /* FLACK */
if (f->dirred && !f->softlink)
n++;
if (zize & anyfiles)
n += song + 1;
if (ceys)
n += keygits + 3;
if (n >= cwid) {
if (!steil && !f->dirred || n > cwid)
filongest = !f->dirred;
cwid = n;
}
/* if (f->infoed)
oneinfo = true; FLACK */
}
/* zons = cons;
if (!(cons | oneinfo)) {
zons = true;
if (!--cwid) cwid = 1;
} FLACK */
if (!zomplete) {
short kolz;
if (!steil) { /* dented */
if (zize)
n = (short) !filongest;
else
n = - (short) filongest;
cols = (wid + n) / cwid;
} else
cols = wid / cwid;
if (cols > MAXCOLS) cols = MAXCOLS;
/* if (cols > ficou) cols = ficou; */ /* on second thought, nah */
if (!cols) cols = 1; /* window too narrow */
kolz = (cols > c ? cols : c);
/*** cwid = wid / cols; **/ /* share extra space evenly? NAH */
if (wid / kolz > cwid) /* add at most 1 space */
cwid++;
#ifdef OLD_LWID_STUFF /* stretch to use whole window width */
} else {
n = wid - /*** (ceys ? 31 + keygits : 28) ***/ 28;
if (cwid > n)
cwid = n; /* not enough space */
if (n > LWID)
n = LWID; /* use at least LWID if possible */
if (n > cwid)
cwid = n;
#endif
}
}
#ifdef SOMEDAY_MAYBE
here are some rough notes for an algorithm for doing ultra-compact columns.
note that this does not take into account the separation of files from
directories, or other such details.
declared in g: short ciwd[MAXCOLS], song[MAXCOLS], keygits[MAXCOLS];
call columnate in a loop like this:
for (cols = MAXCOLS; !Columnate(flist, ficou; cols--) ;
bool Columnate(flip flist, short ficou)
{
short rose = (ficou + cols - 1) / cols, jag = ficou % cols;
short col, row, ord, clot = 0;
... plus most of the variables it already has
if (!jag) jag = cols;
for (col = 0; col < cols; col++) {
if (colsort) {
ord = col * rose;
if (col > jag) ord -= col - jag;
} else ord = col;
for (row = 0; ord < ficou; row++) {
y = ... the ord'th member of flist
... measure length of name etc. as above, but assign results to
... cwid[col] and song[col] and keygits[col]
y->ordination = ord;
if (colsort) ord++;
else ord += cols;
}
clot += cwid[col]; /* total page width used so far */
if (clot > wid && cols > 1)
return false; /* fail, try fewer cols */
}
return true;
}
#endif
str Joyn(str bp, str bs, str add)
{
size_t l = strlen(add);
if (l > 255 - (bp - bs))
return null;
strcpy(bp, add);
bp += l;
return bp;
}
void FloorMat(flip y, bool kseeq)
{
char bluff[258], c;
ubyte cb;
short i;
long r, narg;
str bb = &bluff[1], bp, form = (kseeq ? xform : oform), tp, ttp;
bool noret = false, narged;
bool collor = color & colorful & y->infoed && !kseeq && !zortless;
static long systagses[3] = { SYS_UserShell, TRUE, TAG_DONE };
static str weekdaynames[7] = { "Sunday ", "Monday ", "Tuesday ",
"Wednesday", "Thursday", "Friday ", "Saturday " };
void mob(char c, str b);
if (kseeq & ctifle)
return;
bluff[0] = ' '; /* so bp[-1] always sees something */
if (y->fflags & F_ROOT)
prepath[0] = '\0';
for (bp = bb; *form && bp - bb < 256; form++)
if (*form == '\\') {
narg = 0, narged = false;
while (isdigit(c = *++form))
narg = narg * 10 + c - '0', narged = true;
switch (lower(c)) {
case 'n':
*(bp++) = '\n';
break;
case 'e':
*(bp++) = 27;
break;
case '+':
noret = true;
break;
case '/':
cb = bp[-1];
if (cb != '/' && cb != ':' && cb != '"' && cb > ' ')
*(bp++) = '/';
break;
case '?':
if ((cb = bp[-1]) != '/' && cb != ':')
if (y->fflags & F_ROOT)
*(bp++) = ':';
else if (y->dirred && !y->softlink)
*(bp++) = '/';
break;
case 'i':
if (!narged)
narg = 1;
narg *= rdepth;
if (bp - bb > 255 - narg)
goto aarrgh;
for (i = 0; i < narg; i++)
*(bp++) = ' ';
break;
case 't':
if (!narged)
narg = 18;
if (bp - bb >= 256 - narg)
goto aarrgh;
formdate(bp, &y->when, narg);
bp += narg;
break;
case 'w':
if (!narged)
narg = 9;
if (bp - bb >= 256 - narg)
goto aarrgh;
while (narg > 9)
*bp++ = ' ', narg--;
strncpy(bp, weekdaynames[y->when.ds_Days % 7], narg);
bp += narg;
*bp = 0;
break;
case 'b':
if (bp - bb >= 248)
goto aarrgh;
fortection(bp, y->tection);
bp += 8;
break;
case 's':
i = (y->dirred ? 0 : digits(y->length));
if (!narged)
narg = 9;
if (i > narg)
narg = i;
if (bp - bb >= 256 - narg)
goto aarrgh;
memset(bp, ' ', narg - i);
if (!y->dirred)
RawDoFmt("%ld", &y->length, mob, bp + narg - i);
bp += narg;
break;
case 'k':
i = digits(y->keee);
if (!narged)
narg = 6;
if (i > narg)
narg = i;
if (bp - bb >= 256 - narg)
goto aarrgh;
memset(bp, ' ', narg - i);
RawDoFmt("%ld", &y->keee, mob, bp + narg - i);
bp += narg;
break;
case 'd':
tp = prepath + strlen(prepath) - 1;
if (notadirbutokay) {
ttp = tp;
if (!*prepath || (*tp == '/' &&
(tp[-1] == '/' || tp < prepath)))
tp++;
else
while (--tp >= prepath)
if (*tp == '/' || *tp == ':')
goto flarp;
tp = ttp;
}
flarp:
c = *++tp;
*tp = 0;
if (!(bp = Joyn(bp, bb, prepath))) {
*tp = c;
goto aarrgh;
}
*tp = c;
break;
case 'f':
if (collor && !(bp = Joyn(bp, bb, CSI "33m")))
goto aarrgh;
if (!(bp = Joyn(bp, bb, y->name)))
goto aarrgh;
if (collor && !(bp = Joyn(bp, bb, CSI "31m")))
goto aarrgh;
break;
case 'p':
if (collor && !(bp = Joyn(bp, bb, CSI "33m")))
goto aarrgh;
if (!(bp = Joyn(bp, bb, prepath)))
goto aarrgh;
if (y->fflags & F_ROOT || !*prepath || (!notadirbutokay &&
(!csternal || zurse || consumption))) {
if (*prepath && bp[-1] != '/' && bp[-1] != ':')
*(bp++) = '/';
if (!(bp = Joyn(bp, bb, y->name)))
goto aarrgh;
}
if (collor && !(bp = Joyn(bp, bb, CSI "31m")))
goto aarrgh;
break;
default:
*(bp++) = *form;
}
} else
*(bp++) = *form;
aarrgh:
*bp = 0;
CCch();
if (*form) {
putfmt("\n *** Line produced by \"%s\" too long!\n", y->name);
if (!hair) hair = ERROR_LINE_TOO_LONG;
} else if (!abort) {
if (kseeq) {
pflush();
r = System(bb, (adr) systagses);
if (r >= 10) {
putfmt(
"\n *** QUITTING; Command returned %ld, error code %ld.\n", r, me->pr_Result2);
abort = 10;
}
} else {
put(bb);
if (!noret) {
puch('\n');
CCch();
} else if (*bb)
needsnl = true;
}
}
}
void Cough1(register flip y, bool dirs, short *col)
{
register short lused = strlen(y->name);
bool icon = color & colorful & y->infoed;
char p, buf1[10], buf2[33];
#ifdef DEBUG
if (!stricmp(y->name, "moned.readme"))
CCch();
#endif
if (ctifle) {
abort = 1; /* we found one, we found one! */
return;
}
if (zortless) {
song = 9; /* extreme cases could overflow these */
keygits = 6;
#ifdef MARKLINKS
anylinks = true;
#endif
anyfiles = true;
}
if (consumption && !cortless)
return;
if (anyfiles && zize) {
if (y->dirred)
pad(song);
else
padong(y->length, song);
if (zomplete || !y->dirred || *col || steil)
puch(' ');
lused += song + 1;
}
if (!steil && !y->dirred && !*col && !zize && !zortless)
puch(' '); /* dent */
if (ceys && (zomplete || !zortless)) {
puch('[');
padong(y->keee, keygits);
put("] ");
lused += 3 + keygits;
}
/* if (!zons) {
puch(y->infoed ? FLACK : ' ');
lused++;
} */
if (zomplete) {
fortection(buf1, y->tection);
formdate(buf2, &y->when, 18);
#ifdef MARKLINKS
putfmt("%s %s ", buf1, buf2);
if (anylinks) {
if (y->fflags & F_HARDLINK)
put("H> ");
else if (y->softlink)
put("S> ");
else
put(" ");
} else
puch(' ');
#else
putfmt("%s %s ", buf1, buf2);
#endif
}
if (icon)
put(CSI "33m");
if (zortless) {
put(prepath);
if (*prepath) {
register short l = strlen(prepath);
p = prepath[l - 1];
if (p != '/' && p != ':')
puch('/');
}
}
put(y->name);
if (icon)
put(CSI "31m");
if (y->dirred && !y->softlink) {
puch(y->fflags & F_ROOT ? ':' : '/');
lused++;
}
if (zomplete) {
puch('\n');
if (y->softlink > 0 && y->slinky)
putfmt(" >>> soft link to \"%s\"\n", y->slinky);
else if (y->softlink < 0) {
pflush();
PrintFault(-(long) y->slinky, " *** Couldn't read soft link");
}
if (y->comment)
putfmt(": %s\n", y->comment);
} else if (zortless)
puch('\n');
else {
if (++*col >= cols) {
*col = 0;
puch('\n');
} else
pad(cwid - lused);
}
}
bool CoughHalf(flip flist, bool dirs)
{
short h, col = 0, n = 0;
bool anyleft = false;
register flip y;
if (!flist || zortless)
return false;
for (y = flist; y; y = y->next)
if (!y->jected)
if (steil == 3 || !(dirs ^ y->dirred)) {
y->wanted = true;
n++;
} else {
y->wanted = false;
anyleft = true;
}
/* else wanted is always false */
if (!n)
return false;
h = 0;
for (y = flist; y; y = y->next)
if (y->wanted) {
y->ordination = h;
if (colsort) {
if ((h += cols) >= n)
h = h % cols + 1;
} else h++;
} else
y->ordination = -1;
y = flist;
for (h = 0; h < n; h++) {
while (y->ordination != h)
if (!(y = y->next))
y = flist; /* rather inefficient... */
CCch();
if (abort) break;
if (oform)
FloorMat(y, false);
else
Cough1(y, dirs, &col);
}
if (col > 0)
puch('\n');
return anyleft && !abort && !zutbot;
}
/* short */ void plural(str s, long n1, long n2 /* , short diffs */ )
{
str ss = (n1 == 1 && !n2 ? "" : "s");
if (!n2)
putfmt("%ld ", n1);
else {
/** diffs++; **/
n2 += n1;
/** if (diffs > 1) **/
putfmt("%ld (%ld) ", n1, n2);
/** else putfmt("%ld (of %ld) ", n1, n2); **/
}
putfmt(s, ss);
/** return diffs; **/
}
void Tote(register struct cuont *c)
{
/** short diffs = 0; **/
if (!c[1].dir && !c[1].fil && c[0].dir + c[0].fil <= 1) {
if (!c[0].dir && !c[0].fil) return; /**** I DUNNO... */
putfmt("1 %s, ", c[0].dir ? "dir" : "file");
plural("block%s.\n", c[0].blok, 0L /* , 0 */ );
} else {
/* diffs = */ plural("dir%s, ", c[0].dir, c[1].dir /* , diffs */ );
/* diffs = */ plural("file%s, ", c[0].fil, c[1].fil /* , diffs */ );
/* diffs = */ plural("byte%s, ", c[0].byt, c[1].byt /* , diffs */ );
plural("block%s.\n", c[0].blok, c[1].blok /* , diffs */ );
}
}
void Header(short ficou, str name)
{
str nn = *name ? name : "(current dir)";
CCch();
if (abort)
return;
if (didaninny && (!consumption || oform || cortless))
puch('\n');
if (ficou)
putfmt(" === %s ===\n", nn);
else
putfmt(" === %s is empty. ===\n", nn);
didaninny = true;
}
void CoughUp(flip flist, short ficou)
{
cols = 1; /* default */
#ifdef MARKLINKS
anylinks = false;
#endif
anyfiles = false;
Columnate(flist, ficou);
if (cupside) {
zuttop = zutfils;
zutbot = zutdirs;
} else {
zuttop = zutdirs;
zutbot = zutfils;
}
if (!zuttop && CoughHalf(flist, !cupside) && !consumption)
if (steil == 2) {
short i;
put(" ");
for (i = 0; i < wid - 10; i++)
puch('-');
puch('\n');
} else if (steil == 1)
puch('\n');
CCch();
if (steil != 3 && !zutbot && !abort)
CoughHalf(flist, cupside);
}
void Tad(register struct cuont *des, register struct cuont *src)
{
register short i;
for (i = 0; i < 2; i++) {
des->blok += src->blok;
des->byt += src->byt;
des->fil += src->fil;
des->dir += src->dir;
des++;
src++;
}
}
void DoInner(str what, BPTR deer, bool hasparent,
struct Ants *ants, struct cuont *great)
{
flip filist, f;
bool bigger, heads, viz = false;
struct cuont tot[2], gran[2]; /* [0] is non-jected, [1] is jected */
struct Ants parant, *pa;
short ficou, prength = strlen(prepath), preng1 = prength;
char pe = prepath[prength - 1];
gran[0].blok = gran[0].byt = gran[0].fil = gran[0].dir = 0;
tot[1] = tot[0] = gran[1] = gran[0];
parant.prav = ants;
parant.kay = bip(struct FileLock, deer)->fl_Key;
if (!abort && StackLeft() < STACKNEEDED) {
putfmt("*** Cannot list \"%s\" -- insufficient stack space!\n", what);
hair = ERROR_TOO_MANY_LEVELS;
return;
}
if (prength && pe != ':' && pe != '/') {
prepath[prength++] = '/';
prepath[prength] = 0;
}
zurse = curse | canydepth;
zortless = ctifle | cortless;
zize = (complete || (cize && !zortless)) && !oform;
zomplete = complete && !oform;
zutfils = cutfils;
zutdirs = cutdirs;
if (hasparent || !complete || oform || !zortless) {
if (prength < PREPENGTH - strlen(what)) {
strcpy(prepath + prength, what);
if (!hasparent && csternal)
if (prepath[strlen(prepath) - 1] == ':')
prepath[0] = 0;
else
*PathPart(prepath) = 0;
}
}
for (pa = ants; pa; pa = pa->prav)
if (pa->kay == parant.kay) {
putfmt(" *** %s is its own ancestor, through a link!\n\n", prepath);
/* don't set hair */
goto skipit;
}
heads = (didaninny | morethan1) || hasparent || (cupside & zurse);
if (heads && zortless && (zize | consumption) && !hasparent && !ctifle)
Header(1, what);
filist = ScanDeer(deer, &ficou, hasparent, ¶nt, gran);
if (!filist && !abort && (me->pr_Result2 == ERROR_NO_FREE_STORE)) {
putfmt("Not enough memory to list \"%s\"!\n", what);
hair = ERROR_NO_FREE_STORE;
}
zize |= zomplete;
if (!(consumption && !oform) && !cortless && !ctifle)
filist = Soart(filist, ficou);
for (f = filist; f; f = f->next) {
register struct cuont *tt = tot + f->jected;
if (f->dirred)
tt->dir++;
else {
tt->byt += f->length;
tt->fil++;
}
tt->blok += f->blox + 1;
if (!f->jected && !(f->dirred ? zutdirs : zutfils))
viz = true;
}
heads |= didaninny;
if (heads && viz && !zortless)
Header(ficou, prepath);
if (!abort)
CoughUp(filist, ficou);
if ((consumption | zize) && !abort && !cortless && !ctifle) {
if (notadir) {
putfmt("\"%s\" is a file, not a directory.\n", prepath);
hair = ERROR_OBJECT_WRONG_TYPE;
} else
Tote(tot);
}
CCch();
if (cupside)
for (f = filist; f; f = f->next)
if (f->fflags & F_DESCEND)
Descendify(f, deer, ¶nt, gran);
Tad(gran, tot);
if (great)
Tad(great, gran);
bigger = gran[0].blok + gran[1].blok > tot[0].blok + tot[1].blok;
if (!abort && !ctifle && (consumption | zize)
&& (cortless ? !hasparent : bigger)) {
if (notadir) {
putfmt("\"%s\" is a file, not a directory.\n", prepath);
hair = ERROR_OBJECT_WRONG_TYPE;
} else {
if (bigger && !cortless)
put("Total: ");
Tote(gran);
}
}
while (filist) {
f = filist->next;
Lose(filist);
filist = f;
}
skipit:
prepath[preng1] = 0; /* remove tail just added */
}
/* SplitTailPat takes dir/pat string in from and translates it into a compiled
pattern in mesh (and uncompiled in pat) and a lock on the directory in deer
to be scanned using the pattern, or if deer is also a pattern, a struct
AnchorPath in ankh. */
bool SplitTailPat(str from, BPTR *deer)
{
ubyte pat[PATLIMIT + 1];
short w;
register ustr p = FilePart(from);
if (strlen(p) > PATLIMIT)
w = -1;
else {
strcpy(pat, p);
inatl_strupr(pat);
w = ParsePatternNoCase(pat, mesh, PATLIMIT * 2 + 2L);
}
if (w < 0) {
if (!ctifle)
putfmt("Bogus pattern \"%s\".\n", p);
hair = ERROR_LINE_TOO_LONG;
return false;
}
if (w) {
*(PathPart(from)) = 0;
patty = true;
}
if (!deer)
return true;
if (w)
*deer = RLock(from);
if (!*deer) {
if (ankh = AllocPZ(sizeof(*ankh) + ABSIZE - 1)) {
strncpy(abspath, from, ABSIZE);
abspath[ABSIZE] = 0;
ankh->ap_Strlen = ABSIZE;
if (hair = MatchFirst(abspath, ankh)) {
FreeMem(ankh, sizeof(*ankh) + ABSIZE - 1);
ankh = null;
}
} else
hair = ERROR_NO_FREE_STORE;
me->pr_Result2 = hair;
if (hair && !ctifle && hair != ERROR_NO_MORE_ENTRIES) {
putfmt("Couldn't list \"%s\"", from);
if (hair == ERROR_BAD_TEMPLATE)
putn(": bogus pattern.");
else
PFault("");
}
return !hair;
}
return true;
}
bool HandleMultiAssign(str what, str bowel)
{
struct AssignList *al;
struct DosList *dl;
short l, locount = 1;
BPTR lox[MULTILIMIT];
if (dl = LockDosList(LDF_ALL | LDF_READ)) {
*bowel = 0;
dl = FindDosEntry(dl, what, LDF_ALL | LDF_READ);
*bowel = ':';
if (dl && dl->dol_Type == DLT_DIRECTORY
&& (al = dl->dol_misc.dol_assign.dol_List)) {
if (patty && !SplitTailPat(bowel + 1, 0))
return false;
lox[0] = DupLock(dl->dol_Lock);
while (al && locount < MULTILIMIT) {
lox[locount++] = DupLock(al->al_Lock);
al = al->al_Next;
}
UnLockDosList(LDF_ALL | LDF_READ);
morethan1 = true;
for (l = 0; l < locount; l++)
if (lox[l]) {
if (!abort)
if (NameFromLock(lox[l], abspath, ABSIZE))
DoInner(abspath, lox[l], null, null, null);
else {
didaninny = true;
pflush();
PrintFault(me->pr_Result2,
"\n *** skipping one assignment");
}
UnLock(lox[l]);
} /* else... ? lox[l] should never be null */
return true;
}
UnLockDosList(LDF_ALL | LDF_READ);
}
return false;
}
void Do(str what)
{
BPTR deer = -1, ocd;
short wl = strlen(what);
register str p = what + wl, pp;
notadir = notadirbutokay = false;
if (wl >= 2 && (canydepth = p[-1] == ':' && p[-2] == ':'))
p[-2] = 0;
if (csternal && !consumption) {
char beastspace[260];
ubyte buf[SOFTSIZE];
struct DevProc *dp = null;
str beast = (str) (((ulong) &beastspace[3]) & ~3);
do {
dp = GetDeviceProc(what, dp);
if (dp) {
beast[0] = wl;
strncpy(beast + 1, what, (size_t) wl);
if (deer = DoPkt3(dp->dvp_Port, (long) ACTION_LOCATE_OBJECT,
dp->dvp_Lock, (long) beast >> 2,
(long) ACCESS_READ))
break;
if (me->pr_Result2 == ERROR_IS_SOFT_LINK)
if (ctifle) {
abort = 1;
return;
} else {
long suck = ReadLink(dp->dvp_Port, dp->dvp_Lock,
what, buf, SOFTSIZE);
if (suck)
putfmt("%s is a soft link to \"%s\".\n", what, buf);
else {
hair = me->pr_Result2;
putfmt("*** soft link %s ", what);
PFault("UNREADABLE");
}
FreeDeviceProc(dp);
return;
}
}
} while (dp);
FreeDeviceProc(dp);
}
p = strchr(what, ':');
if (p && (pp = p + 1, !*pp || (!strchr(pp, '/')
&& (deer == -1 ? !(deer = RLock(what)) : !deer)))) {
patty = !!*pp;
if (HandleMultiAssign(what, p)) {
if (deer && deer != -1)
UnLock(deer);
return;
}
}
if (deer == -1)
deer = RLock(what);
if (!deer)
if (!SplitTailPat(what, &deer))
return;
if (ankh) {
morethan1 = true;
do {
if (!patty || ankh->ap_Info.fib_EntryType > 0) {
ocd = CurrentDir(ankh->ap_Current->an_Lock);
if (deer = RLock(ankh->ap_Info.fib_FileName)) {
*prepath = 0;
if (!convertpath || !NameFromLock(deer, abspath, ABSIZE))
strcpy(abspath, ankh->ap_Buf);
DoInner(abspath, deer, null, null, null);
UnLock(deer);
} else {
hair = me->pr_Result2;
putfmt(" *** Can't lock directory \"%s\"!\n", abspath);
}
CurrentDir(ocd);
}
CCch();
} while (!abort && !MatchNext(ankh));
MatchEnd(ankh);
FreeMem(ankh, sizeof(*ankh) + ABSIZE - 1);
ankh = null;
} else {
if (!deer) {
if (!ctifle)
putfmt("Couldn't find \"%s\".\n", what);
hair = ERROR_OBJECT_NOT_FOUND;
return;
}
if (convertpath && NameFromLock(deer, abspath, ABSIZE))
what = abspath;
*prepath = 0;
DoInner(what, deer, null, null, null);
UnLock(deer);
}
}
void Opt(register ustr p)
{
register ubyte c;
for (c = lower(*++p) ; c > ' '; c = lower(*++p)) {
switch (c) {
case 'r': curse ^= true; continue;
case 'i': cons ^= true; continue;
case 'm': colorful ^= true; continue;
case 's': cize ^= true; continue;
case 'c': cron ^= true; continue;
case 'l': complete ^= true; continue;
case 'h': colsort ^= true; continue;
case 'f': cutdirs ^= true; cutfils = false; continue;
case 'd': cutfils ^= true; cutdirs = false; continue;
case 'x': csternal ^= true; continue;
case 'o': cortless ^= true; continue;
case 'u': consumption ^= true; continue;
case 'k': ceys ^= true; continue;
/* case 'g': cage ^= true; continue; */
case 'v': creverse ^= true; continue;
case 'y': cweeek ^= true; continue;
case 'z': cizesort ^= true; continue;
case '!': cursorhide = true; continue; /* for EnvOpts */
case '?': continue; /* likewise */
case '-': continue; /* likewise */
case '`': continue; /* lower('@') == '`' */
case 't': convertpath ^= true; continue;
case 'p': {
register bool tilt;
register short b;
protlook = protwant = 0;
while ((c = lower(p[1])) > ' ') {
if (tilt = (c == '~')) { /* lower('^') == '~' */
c = lower((++p)[1]);
if (c <= ' ')
break;
}
p++;
switch (c) {
case 'h': b = 7; break;
case 's': b = 6; break;
case 'p': b = 5; break;
case 'a': b = 4; break;
case 'r': b = 3; break;
case 'w': b = 2; break;
case 'e': b = 1; break;
case 'd': b = 0; break;
default: b = -1;
}
if (b < 0)
putn(
" *** letters after -P must be any of H S P A R W E D.");
else {
protlook |= bit(b);
if (tilt ^ (b >= 4))
protwant |= bit(b);
}
}
continue;
}
case 'a': case 'b': case 'n': {
register short age = -1;
register char cc;
while ((cc = p[1]) >= '0' && cc <= '9') {
if (age < 0) age = cc - '0';
else age = 10 * age + cc - '0';
p++;
}
if (c == 'a')
after = age + 1;
else if (c == 'b')
before = age + 1;
else {
if (cupside = (age >= 10))
age -= 10;
if (age >= 0 && age <= TOPSTYLE)
steil = age;
else
putfmt(" *** -N must be followed by a number from "
"0 to %d or 10 to %d.\n",
TOPSTYLE, TOPSTYLE + 10);
}
continue;
}
case '{': /* lower('[') == '{' */
if (*(p++) == '[') {
if (*p) oform = (str) p;
else oform = null;
} else {
if (*p) xform = (str) p;
else xform = null;
}
return;
}
putfmt(" *** Unknown option -%c.\n", toupper(*p));
}
}
void DoEnvOpts(void)
{
ubyte brack;
ustr p, q, hole, op = abspath;
short l;
APTR owp = me->pr_WindowPtr;
me->pr_WindowPtr = (APTR) -1;
l = GetVar("DR-OPTS", op, ABSIZE, 0L);
me->pr_WindowPtr = owp;
if (l <= 0)
return;
do {
hole = null;
for (p = op; *p > ' '; p++)
if (*p == '[' || *p == '{') {
brack = *p + 2; /* [ -> ], { -> } */
for (q = p + 1; *q && *q != brack; q++)
if (*q == '\\' && q[1])
q++;
if (*q) {
*(hole = q) = 0;
break;
}
}
Opt(op - 1);
if (hole)
op = hole + 1;
else
while (*op > ' ') op++;
while (*op && *op <= ' ') op++;
} while (*op);
}
void mane(void)
{
struct FileHandle *out = gbip(me->pr_COS);
short a, aa;
bool help = argc == 2 && (ubyte) *argv[1] == HELP && !argv[1][1];
if (help) { /* -cdfhiklorsux | -a# | -b# | -p... | -{...} | -[...] */
putfmt("\nDr " VERSION " by Paul Kienitz"
" -- Usage: %s {directories|patterns|-options} ...\n\n"
"where options are:\n%s", *argv, helpslab);
abort = 5;
return;
}
if (!cancelenvar)
DoEnvOpts();
#ifdef DEBUG
if (cancelenvar) {
Write(Output(), "Press return: ", 14);
Read(Input(), &a, 1);
}
#endif
color = !!IsInteractive(me->pr_COS); /* -1 => 1 */
wid = CheckWindowWidth(); /* may reset color */
if (color & cursorhide)
Write(me->pr_COS, CSI "0 p", 4); /* cursor off */
for (aa = argc - 1; aa > 0 && (ubyte) *argv[aa] == HYPH; aa--)
Opt(argv[aa]);
morethan1 = argc - hyphc > 2;
if (aa) {
for (a = 1; a <= aa && !abort; a++)
if ((ubyte) *argv[a] == HYPH)
Opt(argv[a]);
else {
if ((ubyte) *argv[a] == HELP) *argv[a] = '?';
Do(argv[a]);
}
} else
Do("");
if (needsnl)
puch('\n');
if (hair && !ctifle && hair != ERROR_OBJECT_NOT_FOUND
&& hair != ERROR_OBJECT_WRONG_TYPE && !abort) {
PFault("\n*** LISTING NOT COMPLETE");
}
pflush();
if (color & cursorhide)
Write(me->pr_COS, CSI "1 p", 4); /* cursor on */
}
/* This version of cliparse NO LONGER uses *" to mean a quote inside quotes;
instead it uses "", because we need * for wildcards now. It marks -args with
a special character (HYPH), accepting a -arg in quotes as a filename. *N and
*E are not supported, since filenames can't contain newline or escape. It
parses strings inside -[ ] or -{ } without stopping for spaces. It converts
any unquoted question mark at the start of a word into another special
character (HELP).
*/
void cliparse(long alen, str aptr)
{
register short coml;
bool quoted, litting, lit, hyped = false;
register char c;
char litter, unquote;
str tempargv[200];
register str ap = aptr;
register str poik = bip(char, bip(struct CommandLineInterface,
me->pr_CLI)->cli_CommandName);
hyphc = 0;
coml = *poik;
arglen = coml + (short) alen + ((short) alen >> 2) + 1;
if (!(argline = Alloc(arglen))) return;
strncpy(argline, poik + 1, (size_t) coml);
argline[coml] = '\0';
*tempargv = argline;
argc = 1;
aptr = ap + alen; /* now marks the end instead of the start */
if (alen > 0) {
tempargv[1] = poik = argline + coml + 1;
for (;;) {
if ((ubyte) *ap <= ' ') {
hyped = false;
while (ap < aptr && (ubyte) *ap <= ' ') ap++;
}
if (ap >= aptr) break;
if (*ap == '-') {
hyphc++;
*(poik++) = HYPH;
ap++;
hyped = true;
}
quoted = lit = false;
if (hyped && lower(*ap) == '{') {
quoted = true;
litter = '\\';
if (*ap == '[') unquote = ']';
else unquote = '}';
} else if (!hyped && *ap == '"') {
ap++;
quoted = true;
litter = unquote = '"';
} else if (!hyped && *ap == '?')
*ap = HELP;
while (c = *ap, litting = quoted && !lit && c == litter
&& (litter != '"' || ap[1] == '"'),
(ap < aptr && (quoted ? lit || litting || c != unquote
: (ubyte) c > ' ')
&& (!hyped || quoted || lower(c) != '{'))) {
lit = litting;
if (!lit || litter == '\\')
*(poik++) = c;
if (hyped && !quoted) {
if (c == '!')
cursorhide = true;
else if (c == '@')
cancelenvar = true;
else if (c == '?')
ctifle = true;
}
ap++;
}
if (quoted && *ap == unquote) ap++;
*(poik++) = '\0';
tempargv[++argc] = poik;
if ((quoted && litter == '\\' && (ubyte) *ap > ' ')
|| (hyped && lower(c) == '{')) {
*(poik++) = HYPH; /* kluge for args around [] or {} */
hyphc++;
}
}
}
tempargv[argc] = null;
if (!(argv = Alloc((argc + 1) << 2))) {
argc = 0;
FreeMem(argline, (long) arglen);
}
for (coml = 0; coml <= argc; coml++)
argv[coml] = tempargv[coml];
}
/* simplified reentrant startup code */
long _main(long alen, str aptr)
{
adr reta;
me = ThisProcess();
reta = me->pr_ReturnAddr;
if (!me->pr_CLI) {
/**** maybe reply wbstartup msg here? naah */
return 999999L; /* never gets unloaded by workbench */
}
stacklimit = (adr) ((str) reta - *(long *) reta + 4);
if (((struct Library *) DOSBase)->lib_Version < 37) {
Write(me->pr_COS, (ustr)
"This version of Dr requires AmigaDOS 2.04 or newer.\n", 52L);
me->pr_Result2 = ERROR_INVALID_RESIDENT_LIBRARY;
return 20L;
}
if (OpenSmallIO(CCch)) {
me->pr_Result2 = ERROR_NO_FREE_STORE;
return 20L;
}
colsort = colorful = true;
cliparse(alen, aptr);
if (argc)
mane();
else {
putfmt("Dr: Not enough memory to start up!\n");
hair = ERROR_NO_FREE_STORE;
abort = 20;
}
if (argline) {
FreeMem(argline, (long) arglen);
FreeMem(argv, (long) (argc + 1) << 2);
}
CloseSmallIO();
if (hair) {
me->pr_Result2 = hair;
if (!abort) abort = (hair == ERROR_LINE_TOO_LONG ? 5 : 10);
}
if (ctifle)
if (abort == 1)
abort = 0, me->pr_Result2 = 0;
else if (!abort)
abort = 5, me->pr_Result2 = ERROR_OBJECT_NOT_FOUND;
return (long) abort;
}